package com.woniu.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.woniu.account.dto.User.UserDto;
import com.woniu.account.form.User.RealNameAuthenticationForm;
import com.woniu.account.model.User;
import com.woniu.account.dao.UserDao;
import com.woniu.account.param.User.*;
import com.woniu.account.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.exception.userexp.YogaException;
import com.woniu.util.JwtUtil;
import com.woniu.util.RedisKeyUtil;
import com.woniu.util.RedisUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 杜岷芫
 * @since 2024年07月11日
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private UserDao userDao;
    @Resource
    private RabbitTemplate rabbitTemplate;

    /*
       发送注册验证码的方法
        */

    @Override
    public void CodeRegister(CodeRegisterParam param) throws Exception {
        //创建一把手机号码锁(公平锁)
        RLock phoneLock = redissonClient.getFairLock(RedisKeyUtil.lockRegisterByUserPhone(param.getUserPhone()));
        //创建一把用户名锁（公平锁）
        RLock userNameLock = redissonClient.getFairLock(RedisKeyUtil.lockRegisterByUsername(param.getUserName()));
        //变成多资源锁；这里放进该方法的顺序就是加锁的顺序
        RLock lock = redissonClient.getMultiLock(phoneLock, userNameLock);


        if (lock.tryLock(10, TimeUnit.SECONDS)) {
            //设置10秒；代表成功
            try {//一定要用try块包括成功，finally成功与否都要释放锁

                //首先判断总错误次数
//               if (redisUtil.hasKey(RedisKeyUtil.lockRegisterByUserPhone(param.getUserPhone()))) {
//                   if (Integer.parseInt(redisUtil.get(RedisKeyUtil.lockRegisterByUserPhone(param.getUserPhone()))) >= 5) {
//                       throw new Exception("超过最大次数限制，验证码失效");
//                   }
//               }



                //先判断验证码是否正确
//                if (!param.getCode().equals(redisUtil.get(RedisKeyUtil.registerCodeKey(param.getUserPhone())))) {
//                    //验证码错误，就给他的错误总次数+1
//                    if (redisUtil.hasKey(RedisKeyUtil.loginCodeKey(param.getUserPhone()))) {
//                        redisUtil.incr(RedisKeyUtil.loginCodeKey(param.getUserPhone()));
//                        if (Integer.parseInt(redisUtil.get(RedisKeyUtil.sendNumbers(param.getUserPhone()))) >= 5) {
//                            redisUtil.delete(RedisKeyUtil.registerCodeKey(param.getUserPhone()));
//                        }
//                    } else {
//                        redisUtil.set(RedisKeyUtil.sendNumbers(param.getUserPhone()), "1", 5 * 60);
//                    }
//                    throw new Exception("验证码错误");
//                }
                //判断手机号是否注册过
                QueryWrapper<User> wrapper = new QueryWrapper<User>();
                wrapper.eq("user_phone", param.getUserPhone());

                User userInfo = userDao.selectOne(wrapper);
                if (userInfo != null) {
                    throw new Exception("该手机号已注册");
                }
                //判断用户名是否已经被注册
                QueryWrapper<User> wrapper1 = new QueryWrapper<User>();
                wrapper1.eq("user_name", param.getUserName());
                User userInfo1 = userDao.selectOne(wrapper1);
                if (userInfo1 != null) {
                    throw new Exception("用户名已注册");
                }
                //执行注册
                userInfo = BeanUtil.toBean(param, User.class);
                //对密码加密
                String md5 = String.valueOf(DigestUtil.md5Hex(param.getUserPass()));
                userInfo.setUserPass(md5);
                userInfo.setUserState(1);
                userInfo.setIdCard(param.getIdCard());
                userDao.insert(userInfo);
                //删除验证码
                redisUtil.delete(RedisKeyUtil.registerCodeKey(param.getUserPhone()));
            } finally {
                lock.unlock();//释放锁
            }

        } else {
            //代表失败
            throw new Exception("服务器繁忙");
        }
    }

    @Override
    public UserDto userlogin(UserInfoLoginParam param) {
        QueryWrapper<User> wrapper = new QueryWrapper<User>();
        wrapper.eq("user_name", param.getUserName());
        User user = userDao.selectOne(wrapper);
        if (user == null) {
            throw new YogaException(5002,"尚未注册");
        }
        if (!user.getUserPass().equals(DigestUtil.md5Hex(param.getUserPass())) ){
            throw new YogaException(5005,"密码错误");

        }
        if (!user.getUserName().equals(param.getUserName()) ){
            throw new YogaException(5006,"账号错误");
        }
        UserDto dto = new UserDto();
        dto.setUserId(user.getUserId());
        dto.setUserName(user.getUserName());
        return dto;
    }

    @Override
    public void sendRegisterCode(String mobile) throws Exception {
        //判断手机号码是否在一分钟内是否发送短信
        if (redisUtil.hasKey(RedisKeyUtil.registerCodeOnMinute(mobile))) {
            throw new YogaException(500,"1分钟不能再次发送");
        }
        //判断该手机号码在一小时内的短信发送次数
        if (redisUtil.hasKey(RedisKeyUtil.sendNumbers(mobile))) {
            Integer number = Integer.parseInt(redisUtil.get(RedisKeyUtil.sendNumbers(mobile)));
            if (number >= 5) {
                throw new YogaException(500,"超过发送短信次数,请稍后再试");
            }
        }
        //判断手机号是否注册过
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone", mobile);

        User userInfo = userDao.selectOne(wrapper);

        if (userInfo != null) {
            throw new YogaException(500,"该手机号已注册");
        }
        //将手机号码保存到消息队列中
        rabbitTemplate.convertAndSend("register_phone_queue",mobile);
    }

    public static void main(String[] args) {

    }


    @Override
    public void updateImage(UpdateUserImageParam param) {
        User user = userDao.selectById(param.getUserId());
        user.setUserImage(param.getImage());
        userDao.updateById(user);
    }

    @Override
    public void UpdateNickName(UpdateNickNameParam param) {
        User user = new User();
        user.setUserNickname(param.getUserNickname());
        user.setUserId(param.getUserId());
        userDao.updateById(user);
    }

    @Override
    public void UpdateUserPassword(UpdateUserPasswordParam param) throws Exception {
        User user = userDao.selectById(param.getUserId());
        String newPass= String.valueOf(DigestUtil.md5Hex(param.getUserPass()));
        if (user.getUserPass().equals(newPass)){
            throw new Exception("两次密码一样，请重新输入");
        }
        user.setUserPass(newPass);
        userDao.updateById(user);
    }

    @Override
    public void UpdateUserPhone(String mobile) throws Exception {
        //判断手机号码是否在一分钟内是否发送短信
        if (redisUtil.hasKey(RedisKeyUtil.loginCodeOnMinute(mobile))) {
            throw new YogaException(500,"1分钟不能再次发送");
        }
        //判断该手机号码在一小时内的短信发送次数
        if (redisUtil.hasKey(RedisKeyUtil.sendNumbers(mobile))) {
            Integer number = Integer.parseInt(redisUtil.get(RedisKeyUtil.sendNumbers(mobile)));
            if (number >= 50) {
                throw new YogaException(500,"超过发送短信次数,请稍后再试");
            }
        }
        //判断手机号是否注册过
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone", mobile);

        User userInfo = userDao.selectOne(wrapper);

        if (userInfo == null) {
            throw new YogaException(600,"手机号不匹配");
        }
        //将手机号码保存到消息队列中
        rabbitTemplate.convertAndSend("update_phone_queue",mobile);
    }

    @Override
    public void codeUpdate(CodeUpdateParam param) throws Exception{
        //创建一把手机号码锁(公平锁)
        RLock phoneLock = redissonClient.getFairLock(RedisKeyUtil.lockRegisterByUserPhone(param.getOldPhone()));
        //变成多资源锁；这里放进该方法的顺序就是加锁的顺序
        RLock lock = redissonClient.getMultiLock(phoneLock);
        if (lock.tryLock(10, TimeUnit.SECONDS)) {
            //设置10秒；代表成功
            try {//一定要用try块包括成功，finally成功与否都要释放锁
                //首先判断总错误次数
//                if (redisUtil.hasKey(RedisKeyUtil.loginCodeOnMinute(param.getOldPhone()))) {
//                    if (Integer.parseInt(redisUtil.get(RedisKeyUtil.sendNumbers(param.getOldPhone()))) >= 5) {
//                        throw new Exception("超过最大次数限制，验证码失效");
//                    }
//                }
                //先判断验证码是否正确
//                     if (!param.getCode().equals(redisUtil.get(RedisKeyUtil.registerCodeKey(param.getOldPhone())))) {
//                    //验证码错误，就给他的错误总次数+1
//                    if (redisUtil.hasKey(RedisKeyUtil.sendNumbers(param.getOldPhone()))) {
//                        redisUtil.incr(RedisKeyUtil.sendNumbers(param.getOldPhone()));
//                        if (Integer.parseInt(redisUtil.get(RedisKeyUtil.sendNumbers(param.getOldPhone()))) >= 5) {
//                            redisUtil.zdel(RedisKeyUtil.registerCodeKey(param.getOldPhone()));
//                        }
//                    } else {
//                        redisUtil.set(RedisKeyUtil.sendNumbers(param.getOldPhone()), "1", 5 * 60);
//                    }
//                    throw new Exception("验证码错误");
//                }
                //判断手机号是否注册过
                QueryWrapper<User> wrapper = new QueryWrapper<User>();
                wrapper.eq("user_phone", param.getOldPhone());

                User userInfo = userDao.selectOne(wrapper);
                if (userInfo == null) {
                    throw new Exception("该手机号有误");
                }
                //执行修改
                userInfo = BeanUtil.toBean(param, User.class);
                userInfo.setUserPhone(param.getNewPhone());
                int update = userDao.update(userInfo, wrapper);
                if (update==0){
                    throw new Exception("手机号更新失败");
                }
                //删除验证码
//                redisUtil.zdel(RedisKeyUtil.registerCodeKey(param.getOldPhone()));
            } finally {
                lock.unlock();//释放锁
            }
        } else {
            //代表失败
            throw new Exception("哦豁，服务器繁忙");
        }
    }

    @Override
    public boolean inspectIdCard(String IdCard) {
        int[] WEIGHTS = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        // 校验码
        char[] CHECK_CODES = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

        if (IdCard == null || IdCard.length() != 18) {
            return false;
        }

        // 校验前17位是否为数字，第18位是否为数字或'X'
        for (int i = 0; i < 17; i++) {
            if (!Character.isDigit(IdCard.charAt(i))) {
                return false;
            }
        }
        char lastChar = IdCard.charAt(17);
        if (!(Character.isDigit(lastChar) || lastChar == 'X' || lastChar == 'x')) {
            return false;
        }

        // 计算校验码
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum += (IdCard.charAt(i) - '0') * WEIGHTS[i];
        }
        char checkCode = CHECK_CODES[sum % 11];

        // 验证校验码
        return Character.toUpperCase(lastChar) == checkCode;
    }
    //实名认证
    @Override
    public String userRealNameAuthentication(RealNameAuthenticationForm form) {
        String str = (String) JwtUtil.getUser(form.getCardId(), form.getUserName());
        return str.equals("实名认证通过") ? "实名认证通过" : "实名认证失败，请重试";
    }

    @Override
    public Page<User> pageAllUser(PageAllParam param) throws YogaException {
        if (param.getPageNum() == null) {
            param.setPageNum(1);
        }
        if (param.getPageSize() == null) {
            param.setPageSize(5);
        }
        //拼接条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>();
        if (param.getUserName()==null){
            param.setUserName("");
        }
        wrapper.like("user_name",param.getUserName());
        Page<User> page = new Page<>(param.getPageNum(), param.getPageSize());
        return  baseMapper.selectPage(page,wrapper);
    }

    @Override
    public User loadUsersByIds(Integer userId) {
        User user = baseMapper.selectOne(new QueryWrapper<User>().eq("user_id", userId).eq("user_state", 1));
        return user;
    }

    @Override
    public PageInfo<UserDto> load(LoadUserParam param) {
        PageHelper.startPage(param.getPageNum() == null ? 1 : param.getPageNum(), param.getPageSize() == null ? 6 : param.getPageSize());
        List<UserDto> userDtoList = userDao.load(param);
        PageInfo<UserDto> userDtoPageInfo = new PageInfo<>(userDtoList);
        return userDtoPageInfo;
    }
}
